using System;
using System.Collections.Generic;
using Microsoft.Build.Framework;

namespace PerformanceDude.MSBuildProfiler
{
    public delegate void ProfilerFinishedEventHandler(Build build);

    public class MSBuildProfiler
    {
        public MSBuildProfiler(IEventSource eventSource)
        {
            if (eventSource == null)
            {
                throw new ArgumentNullException("eventSource");
            }

            eventSource.BuildStarted += PerformanceOnBuildStarted;
            eventSource.BuildFinished += PerformanceOnBuildFinishes;
            eventSource.ProjectStarted += PerformanceOnProjectStarted;
            eventSource.ProjectFinished += PerformanceOnProjectFinishes;
            eventSource.TargetStarted += PerformanceOnTargetStarted;
            eventSource.TargetFinished += PerformanceOnTargetFinished;
            eventSource.TaskStarted += PerformanceOnTaskStarted;
            eventSource.TaskFinished += PerformanceOnTaskFinished;
        }

        private Build _build;
        private readonly Stack<Item> itemStack = new Stack<Item>();

        public event ProfilerFinishedEventHandler ProfilerFinished;

        #region Builds

        private void PerformanceOnBuildStarted(object sender, BuildStartedEventArgs e)
        {
            var build = new Build("MSBuild", e.HelpKeyword, e.Message, e.SenderName, e.ThreadId, e.Timestamp);
            itemStack.Push(build);
        }

        private void PerformanceOnBuildFinishes(object sender, BuildFinishedEventArgs e)
        {
            _build = (Build) itemStack.Pop();
            _build.FinishTime = DateTime.UtcNow.Ticks;
            if (ProfilerFinished != null)
            {
                ProfilerFinished(_build);
            }
        }

        #endregion

        #region Projects

        private void PerformanceOnProjectStarted(object sender, ProjectStartedEventArgs e)
        {
            var project = new Project(e.ProjectFile, e.ProjectFile, e.Properties, e.TargetNames, e.HelpKeyword,
                                          e.Message, e.SenderName, e.ThreadId, e.Timestamp);
            itemStack.Push(project);
        }

        private void PerformanceOnProjectFinishes(object sender, ProjectFinishedEventArgs e)
        {
            var project = itemStack.Pop();
            project.FinishTime = DateTime.UtcNow.Ticks;
            itemStack.Peek().Children.Add(project);
        }

        #endregion

        #region Targets

        private void PerformanceOnTargetStarted(object sender, TargetStartedEventArgs e)
        {
            var target = new Target(e.TargetName + ":Target", e.ProjectFile, e.TargetFile, e.TargetName,
                                    e.HelpKeyword, e.Message, e.SenderName, e.ThreadId, e.Timestamp);
            itemStack.Push(target);
        }

        private void PerformanceOnTargetFinished(object sender, TargetFinishedEventArgs e)
        {
            var target = itemStack.Pop();
            target.FinishTime = DateTime.UtcNow.Ticks;
            itemStack.Peek().Children.Add(target);
        }

        #endregion

        #region Tasks

        private void PerformanceOnTaskStarted(object sender, TaskStartedEventArgs e)
        {
            var task = new Task(e.TaskName + ":Task", e.ProjectFile, e.TaskFile, e.TaskName, e.HelpKeyword, e.Message,
                                e.SenderName, e.ThreadId, e.Timestamp);
            itemStack.Push(task);
        }

        private void PerformanceOnTaskFinished(object sender, TaskFinishedEventArgs e)
        {
            var task = itemStack.Pop();
            task.FinishTime = DateTime.UtcNow.Ticks;
            itemStack.Peek().Children.Add(task);
        }

        #endregion
    }
}